#include <iostream>
#include <cstring>
#include <fstream>
#include "dispatcher.h"
#include "cm_net.h"
#include <cstdlib>

using std::string;
using std::cout;
using std::exit;

struct Client : Dispatcher {
	string name;
	string host_addr;
	UdpClient udp_client;

	Client(string name, string host_addr) : Dispatcher(""), name(name), host_addr(host_addr), udp_client(host_addr) {

	}

	virtual cmd_func add_file;
	virtual cmd_func checkout;
	virtual cmd_func checkin;
	virtual cmd_func get;
	virtual cmd_func view_files;
	virtual cmd_func view_versions;
	virtual cmd_func update_owner;
	virtual cmd_func undo_checkout;
	virtual cmd_func view_my_co;
	virtual cmd_func quit;

	string readline() {
		string line;
		while (std::cin && !line.size()) {
			cout << name << " > ";
			std::getline(std::cin, line);
		}
		if (!std::cin)
			::exit(0);
		return line;
	}

	virtual void handle(Error error) {
		if (error != Error_SUCCESS)
			cout << "Error (" << static_cast<int>(error) << ") ";
		switch (error) {
		case Error_SUCCESS:
			cout << "Success\n";
			break;
		case Error_UNKNOWN_COMMAND:
			cout << "unknown command\n";
			break;
		case Error_BAD_ARGS:
			cout << "Bad arguments\n";
			break;
		case Error_FILE_EXISTS:
			cout << "File already exists\n";
			break;
		case Error_FILE_DOES_NOT_EXIST:
			cout << "File does not exist\n";
			break;
		case Error_FILE_LOCKED:
			cout << "File is locked by another user\n";
			break;
		case Error_NOT_AN_OWNER:
			cout << "You are not an owner\n";
			break;
		case Error_ALREADY_OWNER:
			cout << "You are already an owner of this file\n";
			break;
		case Error_ALREADY_CHECKED_OUT:
			cout << "You are already an owner of this file\n";
			break;
		case Error_NOT_CHECKED_OUT:
			cout << "This file is not checked out yet\n";
			break;
		case Error_NO_SUCH_VERSION:
			cout << "The requested version does not exist for this file\n";
			break;
		default:
			cout << "Unknown error\n";
			break;
		}
	}

	void send(string s) {
		udp_client.send(s + " " + name);
		receive_ok();
	}

	string send_receive(string s) {
		send(s);
		return udp_client.receive();
	}

	void receive_ok() {
		string res = udp_client.receive();
		if (res[0]) {
			cout << "fail.\n";
			throw IllegalAction(static_cast<Error>(res[0]));
		}
	}

	TcpChannel send_for_file(string s) {
		send(s);
		cout << "Establishing TCP connection... ";
		TcpChannel ch(host_addr, SERVER_TCP_PORT);
		cout << "connection Established.\n";
		return ch;
	}

	string safe_send_receive(string s) {
		send(s);
		return SafeUdp(udp_client).receive();
	}
};


int main(int argc, char *argv[]) {
	if (argc != 3) {
		cout << "usage: cmclient <username> <server_name>\n";
		return 1;
	}

	cout << "Client is running...\n";
	string user_name = argv[1];
	string server_name = argv[2];
	Client(user_name, server_name).run();
}

static string extract_leaf(string path) {
	size_t start = path.rfind("/");
	if (start == string::npos)
		start = -1;
	return string(path.begin()+start+1, path.end());
}

void Client::add_file(Map<string, string> args) {
	string filename = args["filename"];
	std::ifstream ss(filename.c_str(), std::ifstream::in);
	send_for_file(args["cmd"] + " " + extract_leaf(filename)).write_from(ss);;
}

void Client::checkin(Map<string, string> args) {
	string filename = args["filename"];
	std::ifstream ss(filename.c_str(), std::ifstream::in);
	send_for_file(args["cmd"] + " " + extract_leaf(filename) + " " + args["comment"]).write_from(ss);
}

void write_to_file(TcpChannel& ch, string filename) {
	std::ofstream ss(filename.c_str(), std::ifstream::out);
	ch.read_into(ss);
	ss.close();
}

void Client::checkout(Map<string, string> args) {
	TcpChannel x = send_for_file(args["original command"]);
	write_to_file(x, args["filename"]);
}

void Client::get(Map<string, string> args) {
	TcpChannel x = send_for_file(args["original command"]);
	write_to_file(x, args["filename"]);
}

void Client::view_files(Map<string, string> args) {
	cout << safe_send_receive(args["original command"]);
}

void Client::view_versions(Map<string, string> args) {
	cout << safe_send_receive(args["original command"]);

}

//update_owner + david file1.c
void Client::update_owner(Map<string, string> args) {
	send(args["original command"]);
}

void Client::undo_checkout(Map<string, string> args) {
	send(args["original command"]);
}

//update_owner + david file1.c
void Client::view_my_co(Map<string, string> args) {
	cout << safe_send_receive(args["original command"]);
}


void Client::quit(Map<string, string> args) {
	::exit(0);
}
